#include <algorithm>
#include <cstdlib>
#include <string>
#include "database.h"
#include "drivedatabase.h"
#include <map>
#include <iostream>
#include <iterator>
#include <fstream>
#include "sys/types.h"
#include "sys/param.h"
#include "sys/stat.h"
#include "dirent.h"
#include <vector>
#include <sstream>
#include "math.h"
#include "stdio.h"

using namespace std;

drivedatabase::drivedatabase() {
	root = "./db";
	mkdir(root.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
	string id_path (root + "/id");
	ifstream id_file (id_path.c_str());
	if (!id_file){
		ofstream id_file_out(id_path.c_str());
		id_file_out << 1 << endl;
		id_file_out.close();
	} else {
		id_file.close();
	}
}

drivedatabase::~drivedatabase(){}

bool drivedatabase::create_newsgroup(string group) {
	int ng_id = get_id(root + "/id");
	for ( int i = 1 ; i < ng_id ; i++) {
		if (get_ng_name(i) == group)
			return false;
	}
	if (mkdir(get_path(ng_id).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0){
		string path = get_path(ng_id, "name");
		string id_path = get_path(ng_id) + "id";
		ofstream idfile(id_path.c_str());
		idfile << 1 << endl;
		idfile.close();
		ofstream ofile(path.c_str());
		ofile << group << endl;
		ofile.close();
		idfile.close();
		increment_id(root + "/id");
		return true;
	}
	return false;
}	

int drivedatabase::increment_id(string filepath){
	fstream file;
	file.open(filepath.c_str());
	streampos start_pos = file.tellp();
	int old;
	file >> old;
	int old_log = (int) log10(old);
	int new_log = (int) log10(old + 1);	
	file.seekp(start_pos);
	file << (old + 1);
	if ( old_log != new_log )
		file << endl;
	file.close();
	return old;
}

int drivedatabase::get_id(string filepath){
	fstream file;
	file.open(filepath.c_str());
	int id;
	file >> id;
	if (!file.good()){
		cout << "read from " << filepath << " failed." << endl;
	}
	file.close();
	return id;
}

pair<bool, unsigned int> drivedatabase::put(const unsigned int group, const string title, const string author, const string text){
	ifstream file (get_path(group).c_str());
	if (!file){
		file.close();
		return make_pair(false, 0);
	} else 
		file.close();
	string id_path = get_path(group) + "id";
	int new_id = get_id(id_path);
	increment_id(id_path);
	stringstream strstream;
	strstream << get_path(group);
	strstream << new_id;
	string s;
	strstream >> s;
	ofstream ofile (s.c_str());
	ofile << title << endl << author << endl << text << endl;
	ofile.close();
	return make_pair(true, new_id);
}


pair<int, map<string, string> > drivedatabase::get(const unsigned int group_id, const unsigned int article_id) {
	map<string, string> m;
	string ng_path (get_path(group_id));
	ifstream ng (ng_path.c_str());
	if (!ng){
		ng.close();
		return make_pair(1, m);
}
	ng.close();
	stringstream strstream;
	strstream << ng_path << "/";
	strstream << article_id;
	string art_path;	
	strstream >> art_path;
	ifstream art (art_path.c_str());
	if (!art){
		art.close();
		return make_pair(2, m);
	}
	string line;
	getline(art, line);
	m["title"] = line;
	getline(art, line);
	m["author"] = line;
	string text = "";
	if (getline(art, line)){
		text += line;
	}
	while (getline(art, line))
		text += "\n" + line;
	m["text"] = text;
	art.close();
	return make_pair(0,m);
}


vector<pair<unsigned int, string> > drivedatabase::get_newsgroups() {
	vector<string> ngs;
	ngs = list(root, 1);

	vector<string>::iterator its = ngs.begin();
	while(its!=ngs.end()){
		++its;
	}

	vector<pair<unsigned int, string> > v;
	for (vector<string>::iterator it = ngs.begin() ; it != ngs.end() ; ++it){
		if ( (*it).at(0) != '.' ){
			string path (root + "/" + *it + "/name");
			ifstream file (path.c_str());
			string name;
			getline(file,name);
			unsigned int id;
			stringstream strstream (*it);
			strstream >> id;
			v.push_back(make_pair(id, name));
			file.close();
		} 
	}
	sort(v.begin(), v.end()); // borde inte behöver göras
	// men eftersom testprogrammet kräver att de kommer i den ordning
	// de lagts in i, så får vi göra det...
	return v;
}


bool drivedatabase::remove_newsgroup(const unsigned int group_id) {
	string path (get_path(group_id));
	ifstream file (path.c_str());
	if (!file){
		file.close();
		return false;
	}
	file.close();
	string command ("rm -rf " + get_path(group_id));
	system(command.c_str());
}


vector<pair<unsigned int, string> > drivedatabase::get_news_entries(unsigned int group){
	vector<pair<unsigned int, string> > v;
	string ng_path (get_path(group));
	ifstream ng (ng_path.c_str());
	if (!ng) {
		ng.close();
		v.push_back(make_pair(0, ""));
		return v;
	}
	vector<string> vt = list(ng_path, 2);
	for ( vector<string>::iterator it = vt.begin() ; it != vt.end() ; ++it) {
		char first_char = (*it).at(0);
		if ( first_char >= '0' && first_char <= '9') {
			string this_path (ng_path + *it);
			ifstream file (this_path.c_str());
			string title;
			//getline(file,title); // skip author..
			getline(file,title);
			stringstream strstream (*it);
			unsigned int id;
			strstream >> id;
			v.push_back(make_pair(id, title));
			file.close();
		}
	}
	sort(v.begin(), v.end());
	return v;
}

int drivedatabase::remove(const unsigned int group_id, const unsigned int article_id) {
	string ng_path (get_path(group_id));
	ifstream ng (ng_path.c_str());
	if (!ng){
		ng.close();
		return 1;
	}
	ng.close();
	stringstream strstream;
	strstream << ng_path;
	strstream << article_id;
	string art_path;	
	strstream >> art_path;
	ifstream art (art_path.c_str());
	if (!art){
		art.close();
		return 2;
	}
	string command ("rm " + art_path);
	system(command.c_str());
	art.close();
	return 0;
}



bool drivedatabase::ng_exist(int ng_id){
	ifstream infile;
	string path = get_path(ng_id, "name");
	infile.open(path.c_str());
	if (!infile.good()){
		infile.close();
		return false;
	}
	infile.close();
	return true;
}

string drivedatabase::get_path(int ng_id, string file){
	stringstream pathstream;
	pathstream << root << "/" << ng_id << "/" << file;
	string path;
	pathstream >> path;
	return path;
}


vector<string> drivedatabase::list(string path, int type) {
	vector<string> v;int status;
   	 struct stat st_buf;
	    DIR *pdir = NULL;
	    pdir = opendir (path.c_str());
	    struct dirent *pent = NULL;
	    if (pdir == NULL)
	    {
		printf ("\nERROR! pdir could not be initialised correctly");
	    }
	 	
	    while (pent = readdir (pdir)) 
	    {
		stringstream ss;
		ss << path << "/" << pent->d_name;
		string file_path;
		ss >> file_path;
		status = file_status(file_path.c_str());
		if (status == 2 && (type == 0 || type == 2)) {
			v.push_back(pent->d_name);
		}
		if (status == 1 && (type == 0 || type == 1)) {
			v.push_back(pent->d_name);
		}
	    }
	    closedir (pdir);
	return v;	
}

string drivedatabase::get_ng_name(int ng_id){
	ifstream infile;
	string path = get_path(ng_id, "name");
	infile.open(path.c_str());
	if (!infile.good()){
		infile.close();
		return "";
	}
	string s;
	getline(infile, s);
	infile.close();
	return s;
}

int drivedatabase::file_status (string str) {
	int status;
	struct stat st_buf;
	status = stat (str.c_str(), &st_buf);

	if (status != 0) {
		return -1;
	}


	if (S_ISREG (st_buf.st_mode)) {
		return 2;
	}
	if (S_ISDIR (st_buf.st_mode)) {
		return 1;
	}
	return 0;
}

